﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Security.Cryptography;

namespace Acms.Bll.Tests
{

    public abstract class Base
    {
        protected DBDataContext _Context;
        protected DBDataContext _FreshContext;

        [TestInitialize]
        public void BaseInit()
        {   
            if (_Context == null)
            {
                _Context = new DBDataContext();
            }
            //do this to stop unique contraint problems
            _Context.PostPositions.DeleteAllOnSubmit(_Context.PostPositions);
            _Context.SubmitChanges();
            _FreshContext = new DBDataContext();

            _TestSites = EnsureSiteData(_Context);
            _TestUsers = EnsureUsersData(_Context);
            _TestUserLoginKeys = EnsureLoginKeyData(_Context);
            _TestEmailActivationCodes = EnsureEmailActivationCodeData(_Context);

            _TestTemplates = EnsureTemplateData(_Context);
            _TestPosts = EnsurePostsData(_Context);
            _Context.SubmitChanges();
        }

        /// <summary>
        /// 0 - Home Page Retired
        /// 1 - Home Page Published
        /// 2 - Home Page Future
        /// 3 - Home Page Pending
        /// 4 - Child 1 Published
        /// 5 - Child 1 Pending
        /// 6 - Child 2 Published
        /// 7 - Child 2 Pending
        /// 8 - Child 1_1 Pending
        /// 9 - Child 1_2 Pending
        /// 10 - Child 1_1_1 Pending
        /// </summary>
        protected List<TestData.Post> _TestPosts;
        private List<TestData.Post> EnsurePostsData(DBDataContext context)
        {
            int noToGen = 11;
            var tmp = context.Posts.OrderBy(x => x.Id).Take(noToGen).ToList();
            context.Posts.DeleteAllOnSubmit(context.Posts.OrderBy(x => x.Id).Skip(noToGen));
            context.PostPositions.DeleteAllOnSubmit(context.PostPositions.Where(x => context.Posts.OrderBy(y => y.Id).Skip(noToGen).Select(y => y.Id).Contains(x.PostId)));
            while (tmp.Count() < noToGen)   
            {
                tmp.Add(new TestData.Post());
                context.Posts.InsertOnSubmit(tmp.Last());
            }

            //1 to 4 - Home Page
            SetBasicPostProperties(tmp[0], tmp[0].PostGuid, 0, "Welcome", "~", "Home Page Notes", "Home Page Summary", null, DateTime.Parse("24 July 2008 13:00").ToUniversalTime(), DateTime.Parse("25 July 2008 01:00").ToUniversalTime());
            SetBasicPostProperties(tmp[1], tmp[0].PostGuid, 0, "Welcome", "~", "Home Page Notes", "Home Page Summary", null, tmp[0].PublishTo, DateTime.Parse("1 January 2025 14:00").ToUniversalTime());
            SetBasicPostProperties(tmp[2], tmp[0].PostGuid, 0, "Welcome", "~", "Home Page Notes", "Home Page Summary", null, tmp[1].PublishTo, null);
            SetBasicPostProperties(tmp[3], tmp[0].PostGuid, 0, "Welcome", "~", "Home Page Notes", "Home Page Summary", null, null, null);

            ////4 & 5 - Child 1
            SetBasicPostProperties(tmp[4], tmp[4].PostGuid, 0, "Child 1", "Child_1", "Child 1 Notes", "Child 1 Summary", tmp[0].PostGuid, tmp[0].PublishFrom, null);
            SetBasicPostProperties(tmp[5], tmp[4].PostGuid, 0, "Child 1", "Child_1", "Child 1 Notes", "Child 1 Summary", tmp[0].PostGuid, null, null);

            //6 & 7 - Child 2
            SetBasicPostProperties(tmp[6], tmp[6].PostGuid, 1, "Child 2", "Child_2", "Child 2 Notes", "Child 2 Summary", tmp[0].PostGuid, tmp[0].PublishFrom, null);
            SetBasicPostProperties(tmp[7], tmp[6].PostGuid, 1, "Child 2", "Child_2", "Child 2 Notes", "Child 2 Summary", tmp[0].PostGuid, null, null);

            /// 8 - Child 1_1 Pending
            SetBasicPostProperties(tmp[8], tmp[8].PostGuid, 0, "Child 1-1", "Child_1_1", "Child 1 1 Notes", "Child 1 1 Summary", tmp[4].PostGuid, null, null);

            /// 9 - Child 1_2 Pending
            SetBasicPostProperties(tmp[9], tmp[9].PostGuid, 1, "Child 1-2", "Child_1_2", "Child 1 2 Notes", "Child 1 2 Summary", tmp[4].PostGuid, null, null);

            /// 10 - Child 1_1_1 Pending
            SetBasicPostProperties(tmp[10], tmp[10].PostGuid, 0, "Child 1-1-1", "Child_1_1_1", "Child 1 1 1 Notes", "Child 1 1 1 Summary", tmp[8].PostGuid, null, null);

            EnsurePostsPositionData(tmp, context);
            /*context.PostPositions.DeleteAllOnSubmit(tmp[0].PostPositions.Skip(1));
            context.PostPositions.DeleteAllOnSubmit(tmp[1].PostPositions.Skip(1));
            context.PostPositions.DeleteAllOnSubmit(tmp[2].PostPositions.Skip(1));
            context.PostPositions.DeleteAllOnSubmit(tmp[3].PostPositions.Skip(1));

            if (tmp[0].PostPositions.Count == 0)
            {
                tmp[0].PostPositions.Add(new TestData.PostPosition());
                tmp[0].PostPositions[0].Id = Guid.NewGuid();
            }
            if (tmp[1].PostPositions.Count == 0)
            {   
                tmp[1].PostPositions.Add(new TestData.PostPosition());
                tmp[1].PostPositions[0].Id = Guid.NewGuid();
            }
            if (tmp[2].PostPositions.Count == 0)
            {   
                tmp[2].PostPositions.Add(new TestData.PostPosition());
                tmp[2].PostPositions[0].Id = Guid.NewGuid();
            }
            if (tmp[3].PostPositions.Count == 0)
            {
                tmp[3].PostPositions.Add(new TestData.PostPosition());
                tmp[3].PostPositions[0].Id = Guid.NewGuid();
            }
            tmp[0].PostPositions[0].ParentPath = tmp[1].PostPositions[0].ParentPath = tmp[2].PostPositions[0].ParentPath = tmp[3].PostPositions[0].ParentPath = null;
            tmp[0].PostPositions[0].ParentSequence = tmp[1].PostPositions[0].ParentSequence = tmp[2].PostPositions[0].ParentSequence = tmp[3].PostPositions[0].ParentSequence = null;
            tmp[0].PostPositions[0].PublishDate = tmp[0].PublishFrom;
            tmp[1].PostPositions[0].PublishDate = tmp[1].PublishFrom;
            tmp[2].PostPositions[0].PublishDate = tmp[2].PublishFrom;
            tmp[3].PostPositions[0].PublishDate = tmp[3].PublishFrom;
            tmp[0].PostPositions[0].Sequence = tmp[1].PostPositions[0].Sequence = tmp[2].PostPositions[0].Sequence = tmp[3].PostPositions[0].Sequence = null;

            ////Child 1
            SetBasicPostProperties(tmp[4], tmp[4].PostGuid);
            SetBasicPostProperties(tmp[5], tmp[4].PostGuid);

            tmp[4].PublishFrom = tmp[0].PublishFrom;
            tmp[4].PublishTo = null;
            tmp[5].PublishFrom = tmp[5].PublishTo = null;
            
            SetChildProperties(tmp[0], tmp[4], 0, new DateTime[] { tmp[0].PublishFrom.Value, tmp[1].PublishFrom.Value, tmp[2].PublishFrom.Value }, context);
            SetChildProperties(tmp[0], tmp[5], 0, new DateTime[0], context);



            ////Child 2
            SetBasicPostProperties(tmp[6], tmp[6].PostGuid);
            SetBasicPostProperties(tmp[7], tmp[6].PostGuid);
            tmp[6].PublishFrom = tmp[0].PublishFrom;
            tmp[6].PublishTo = null;
            tmp[7].PublishFrom = tmp[7].PublishTo = null;
            SetChildProperties(tmp[0], tmp[6], 1, new DateTime[] { tmp[0].PublishFrom.Value, tmp[1].PublishFrom.Value, tmp[2].PublishFrom.Value }, context);
            SetChildProperties(tmp[0], tmp[7], 1, new DateTime[0], context);

            /// 8 - Child 1_1 Pending
            SetBasicPostProperties(tmp[8], tmp[8].PostGuid);
            SetChildProperties(tmp[5], tmp[8], 0, new DateTime[0], context);
            /// 9 - Child 1_2 Pending
            SetBasicPostProperties(tmp[9], tmp[9].PostGuid);
            SetChildProperties(tmp[5], tmp[9], 1, new DateTime[0], context);
            /// 10 - Child 1_1_1 Pending
            //SetBasicPostProperties(tmp[10], tmp[10].PostGuid);
            //SetChildProperties(tmp[8], tmp[10], 0, new DateTime[0], context);*/


            return tmp;
        }

        private void EnsurePostsPositionData(List<TestData.Post> posts, DBDataContext context)
        {
            var dates = posts.Where(x => x.PublishFrom != null).Select(x => x.PublishFrom.Value).Distinct().OrderBy(x => x);

            foreach (var post in posts)
            {
                if (post.PublishFrom == null)
                {
                    context.PostPositions.DeleteAllOnSubmit(post.PostPositions.Skip(1));
                    if (post.PostPositions.Count() == 0)
                    {
                        post.PostPositions.Add(new TestData.PostPosition() { Id = Guid.NewGuid() });
                    }

                    if (post.ParentGuid != null)
                    {
                        var parent = posts.Where(x => x.PublishFrom == null && x.PostGuid == post.ParentGuid).Single();
                        SetPostPositionParentProperties(post.PostPositions[0], parent, null);
                    }
                    else
                    {
                        post.PostPositions[0].ParentPath = null;
                        post.PostPositions[0].ParentSequence = null;
                    }
                    post.PostPositions[0].PublishDate = null;
                    post.PostPositions[0].Sequence = post.Sequence;
                }
                else
                {
                    var datesToAdd = dates.Where(x => x >= post.PublishFrom.Value && (post.PublishTo == null || x < post.PublishTo.Value)).ToList();
                    context.PostPositions.DeleteAllOnSubmit(post.PostPositions.Skip(datesToAdd.Count()));
                    while (post.PostPositions.Count() < datesToAdd.Count())
                    {
                        post.PostPositions.Add(new TestData.PostPosition() { Id = Guid.NewGuid() });
                    }
                    foreach (var date in datesToAdd)
                    {
                        var pp = post.PostPositions.ElementAt(datesToAdd.IndexOf(date));
                        if (post.ParentGuid != null)
                        {
                            var parent = posts.Where(x => x.PublishFrom <= date && ((x.PublishTo ?? DateTime.MaxValue) > date) && x.PostGuid == post.ParentGuid).OrderBy(x => x.PublishFrom).First();
                            SetPostPositionParentProperties(pp, parent, date);
                        }
                        else
                        {
                            pp.ParentPath = null;
                            pp.ParentSequence = null;
                        }
                        pp.PublishDate = date;
                        pp.Sequence = post.Sequence;
                    }
                }
            }
        }

        private void SetPostPositionParentProperties(TestData.PostPosition pp, TestData.Post parent, DateTime? date)
        {
            pp.ParentPath = string.Concat(parent.PostPositions.Where(x => x.PublishDate == date).First().ParentPath, parent.Name, "/");
            pp.ParentSequence = string.Concat(parent.PostPositions.Where(x => x.PublishDate == date).First().ParentSequence, parent.Sequence);
        }

        private void SetBasicPostProperties(TestData.Post post, Guid guid, int sequence, string displayName, string name, string notes, string summary, Guid? parentGuid, DateTime? publishFrom, DateTime? publishTo)
        {
            post.Site = _TestSites[0];
            post.Deleted = false;
            post.DeveloperLocked = false;
            post.CreatedDateTime = DateTime.Parse("24 October 1975 12:00");
            post.Template = _TestTemplates[0];
            post.Sequence = sequence;
            post.Name = name;
            post.DisplayName = displayName;
            post.Notes = notes;
            post.PublishFrom = publishFrom;
            post.PublishTo = publishTo;
            post.ParentGuid = parentGuid;
            post.Summary = summary;
            post.RequiresPublishing = publishFrom == null;
            if (guid != Guid.Empty)
            {
                post.PostGuid = guid;
            }
            else
            {
                post.PostGuid = Guid.NewGuid();
            }
        }

        protected List<TestData.Template> _TestTemplates;
        private List<TestData.Template> EnsureTemplateData(DBDataContext context)
        {
            int noToGen = 1;
            var tmp = context.Templates.OrderBy(x => x.Id).Take(noToGen).ToList();
            context.Templates.DeleteAllOnSubmit(context.Templates.OrderBy(x => x.Id).Skip(noToGen));

            while (tmp.Count() < noToGen)
            {
                tmp.Add(new TestData.Template());
                context.Templates.InsertOnSubmit(tmp.Last());
            }

            for (int i = 0; i < tmp.Count(); i++)
            {
                tmp[i].Deleted = false;
                tmp[i].ExcludeFromSearch = false;
                tmp[i].Name = string.Format("Template {0}", i);
                tmp[i].NodeType = 1;
                tmp[i].Notes = string.Format("Notes for Template {0}", i);
                tmp[i].Site = _TestSites[0];
            }

            return tmp;
        }

        protected List<TestData.Site> _TestSites;
        private List<TestData.Site> EnsureSiteData(DBDataContext context)
        {
            int noToGen = 2;
            var tmp = context.Sites.OrderBy(x => x.Id).Take(noToGen).ToList();
            context.Sites.DeleteAllOnSubmit(context.Sites.OrderBy(x => x.Id).Skip(noToGen));

            while (tmp.Count() < noToGen)
            {
                tmp.Add(new TestData.Site());
                context.Sites.InsertOnSubmit(tmp.Last());
            }

            for (int i = 0; i < tmp.Count(); i++)
            {
                tmp[i].Deleted = false;
                tmp[i].Name = string.Format("Test Site {0}", i);
                tmp[i].Root = string.Format("localhost/TestSite{0}", i);
            }

            return tmp;
        }


        protected List<TestData.User> _TestUsers;
        private List<TestData.User> EnsureUsersData(DBDataContext context)
        {
            int noToGen = 5;
            var tmp = context.Users.OrderBy(x => x.Id).Take(noToGen).ToList();
            context.Users.DeleteAllOnSubmit(context.Users.OrderBy(x => x.Id).Skip(noToGen));

            while (tmp.Count() < noToGen)
            {
                tmp.Add(new TestData.User());
                context.Users.InsertOnSubmit(tmp.Last());
            }

            for (int i = 0; i < tmp.Count(); i++)
            {
                tmp[i].Address1 = string.Format("Address {0}", i);
                tmp[i].Address2 = string.Format("Address Line 2 {0}", i);
                tmp[i].County = string.Format("County {0}", i);
                tmp[i].Created = DateTime.Parse("24/10/1975").ToUniversalTime();
                tmp[i].CustomBool1 = i % 2 == 0 ? true : false;
                tmp[i].CustomBool2 = i % 2 == 1 ? true : false;
                tmp[i].CustomBool3 = i % 2 == 0 ? true : false;
                tmp[i].CustomInt1 = i + 1;
                tmp[i].CustomInt2 = i + 2;
                tmp[i].CustomInt3 = i + 3;
                tmp[i].CustomString1 = string.Format("String {0}", i + 1);
                tmp[i].CustomString2 = string.Format("String {0}", i + 2);
                tmp[i].CustomString3 = string.Format("String {0}", i + 3);
                tmp[i].DateOfBirth = DateTime.Parse(string.Format("24/10/19{0}", i + 80)).ToUniversalTime();
                tmp[i].Deleted = false;
                tmp[i].EmailAddress = string.Format("EmailAddress{0}@test.com", i);
                //tmp[1].EmailAuthenticated = i % 4 == 0 ? true : false;
                tmp[1].EmailAuthenticated = false;
                tmp[i].FirstName = string.Format("FirstName{0}", i);
                tmp[i].Gender = (byte)(i % 2 == 0 ? Bll.User.Genders.Male : Bll.User.Genders.Female);
                tmp[i].LastName = string.Format("LastName{0}", i);
                tmp[i].MobileTel = string.Format("07974 248 40{0}", i);
                tmp[i].PostCode = string.Format("SP{0} 4TF", i);
                tmp[i].Site = _TestSites[0];
                tmp[i].Town = string.Format("Town{0}", i);
                tmp[i].UserName = string.Format("UserName{0}", i);
                tmp[i].Salt = 863274684 + i;
                tmp[i].Password = CreateEncodedPassword(string.Format("Password{0}", i), tmp[i].Salt);
                tmp[i].LastActivity = DateTime.Parse("24/10/1975 14:00").ToUniversalTime();
                tmp[i].LastChangedPassword = i % 3 == 0 ? null : new DateTime?(DateTime.Parse("24/10/1975 10:00").ToUniversalTime());
                tmp[i].LastLocked = i % 4 == 0 ? null : new DateTime?(DateTime.Parse("24/10/1975 8:00").ToUniversalTime());
                tmp[i].LastLogin = DateTime.Parse("24/10/1975 19:00").ToUniversalTime();
            }

            return tmp;
        }

        private string CreateEncodedPassword(string password, int salt)
        {
            // Create Byte array of password string
            ASCIIEncoding encoder = new ASCIIEncoding();
            Byte[] secretBytes = encoder.GetBytes(password);

            // Create a new salt
            Byte[] saltBytes = new Byte[4];
            saltBytes[0] = (byte)(salt >> 24);
            saltBytes[1] = (byte)(salt >> 16);
            saltBytes[2] = (byte)(salt >> 8);
            saltBytes[3] = (byte)(salt);

            // append the two arrays
            Byte[] toHash = new Byte[secretBytes.Length + saltBytes.Length];
            Array.Copy(secretBytes, 0, toHash, 0, secretBytes.Length);
            Array.Copy(saltBytes, 0, toHash, secretBytes.Length, saltBytes.Length);

            SHA1 sha1 = SHA1.Create();
            Byte[] computedHash = sha1.ComputeHash(toHash);
            return encoder.GetString(computedHash);
        }

        protected List<TestData.UserLoginKey> _TestUserLoginKeys;
        private List<TestData.UserLoginKey> EnsureLoginKeyData(DBDataContext context)
        {
            int noToGen = 2;
            var tmp = context.UserLoginKeys.OrderBy(x => x.UserId).Take(noToGen).ToList();

            while (tmp.Count < noToGen)
            {
                tmp.Add(new TestData.UserLoginKey(){Key = Guid.NewGuid()});
                context.UserLoginKeys.InsertOnSubmit(tmp.Last());
            }

            for (int i = 0; i < tmp.Count; i++)
            {
                tmp[i].Perminant = i % 2 == 0 ? true : false;
                tmp[i].User = _TestUsers[0];
                tmp[i].LoggedIn = DateTime.Now.AddMinutes(-19).ToUniversalTime();
                tmp[i].LastAccessed = DateTime.Now.AddMinutes(-1).ToUniversalTime();
            }
            
            context.UserLoginKeys.DeleteAllOnSubmit(context.UserLoginKeys.OrderBy(x => x.UserId).Skip(noToGen));
            return tmp;
        }

        protected List<TestData.EmailActivationCode> _TestEmailActivationCodes;
        private List<TestData.EmailActivationCode> EnsureEmailActivationCodeData(DBDataContext context)
        {
            int itemsToCreate = 6;
            var tmp = context.EmailActivationCodes.OrderBy(x => x.Id).Take(itemsToCreate).ToList();

            while (tmp.Count < itemsToCreate)
            {
                tmp.Add(new TestData.EmailActivationCode());
                context.EmailActivationCodes.InsertOnSubmit(tmp.Last());
            }

            for (int i = 0; i < itemsToCreate; i++)
            {
                tmp[i].Activated = i > _TestUsers.Count() ? new DateTime?(DateTime.Parse("24/10/1975 20:00").ToUniversalTime()) : null;
                tmp[i].Code = string.Format("TestCode{0}", i);
                tmp[i].Created = DateTime.Parse("24/10/2008 12:00:00").ToUniversalTime();
                tmp[i].Deleted = false;
                tmp[i].EmailAddress = string.Format("testEmailActivated{0}@email.com", i);
                tmp[i].User = _TestUsers[i % _TestUsers.Count()];
                if (tmp[i].Activated != null)
                {
                    tmp[i].User.EmailAuthenticated = true;
                }
            }
            context.EmailActivationCodes.DeleteAllOnSubmit(context.EmailActivationCodes.OrderBy(x => x.Id).Skip(itemsToCreate));

            return tmp;
        }

    }
}
